using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace MR_HKSFilereader
{
    public class OffReader
    {
        // variables
        string fileName;
        List<int>[] connectedVertices;
        int nrVertices;
        int nrFaces;

        // constructor
        public OffReader(string fn)
        {
            this.fileName = fn;
        }

        // methods

        private void SaveToVertexList(int index, int connectedVertex)
        {
            if (!connectedVertices[index].Contains(connectedVertex))
            {
                connectedVertices[index].Add(connectedVertex);
            }
        }

        public void ReadFile()
        {
            TextReader reader = new StreamReader(fileName);

            //skip first line
            string line = reader.ReadLine();

            //second line contains vertices and faces counts.
            line = reader.ReadLine();
            string[] split = line.Split(" ".ToCharArray());
            nrVertices = Convert.ToInt32(split[0]);
            nrFaces = Convert.ToInt32(split[1]);

            //skip all vertex descriptions
            for (int i = 0; i < nrVertices; i++)
            {
                reader.ReadLine();
            }

            line = reader.ReadLine();

            //construct the data array containing all connected vertices for each vertex
            connectedVertices = new List<int>[nrVertices];
            for (int i = 0; i < connectedVertices.Length; i++)
            {
                connectedVertices[i] = new List<int>();
            }

            //read all face lines
            while (line != null)
            {
                split = line.Split(" ".ToCharArray());
                int v1 = Convert.ToInt32(split[1]);
                int v2 = Convert.ToInt32(split[2]);
                int v3 = Convert.ToInt32(split[3]);
                SaveToVertexList(v1, v2);
                SaveToVertexList(v1, v3);
                SaveToVertexList(v2, v1);
                SaveToVertexList(v2, v3);
                SaveToVertexList(v3, v1);
                SaveToVertexList(v3, v2);

                //read new line
                line = reader.ReadLine();
            }

            reader.Close();
        }

        public int getNumberOfVertices()
        {
            return nrVertices;
        }

        public int getNumberOfFaces()
        {
            return nrFaces;
        }

        public void PrintInfo()
        {
            //print info of all vertices
            int totalConnections = 0;
            for (int i = 0; i < connectedVertices.Length; i++)
            {
                //Console.WriteLine("vertex " + i + " has " + connectedVertices[i].Count + " connections");
                totalConnections += connectedVertices[i].Count;
            }
            totalConnections = totalConnections / 2;
            Console.WriteLine("Number of vertices: " + getNumberOfVertices());
            Console.WriteLine("Number of faces: " + getNumberOfFaces());
            Console.WriteLine("Total connections: " + totalConnections);
        }

        public int[,] LaplacianMatrix()
        {
            int length = connectedVertices.Length;
            int[,] matrix = new int[length, length];
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    matrix[i, j] = i == j ? connectedVertices[i].Count : connectedVertices[i].Contains(j) ? -1 : 0;
                }
            }
            return matrix;
        }

        public void LaplacianToFile()
        {
            StreamWriter s = new StreamWriter(fileName.Replace(".", "") + ".dat");
            int length = connectedVertices.Length;
            for (int j = 0; j < length; j++)
            {
                
                for (int i = 0; i < length; i++)
                {
                    int val = (i == j ? connectedVertices[i].Count : connectedVertices[i].Contains(j) ? -1 : 0);
                    if (val != 0)
                    {
                        s.Write((i+1) + " " + (j+1) + " " + val);
                        if (j != length - 1 || i != length - 1)
                            s.WriteLine();
                    }
                }
            }
            s.Flush();
            s.Close();
        }

        public void SaveToSerializedMatrix()
        {
            int[,] matrix = LaplacianMatrix();
            
            // serialize and save matrix
            // File will be saved to same path as off-file, but with another extension
            string serializeFileName = Path.ChangeExtension(fileName, ".mtrx");
            Stream stream = File.Open(serializeFileName, FileMode.Create);
            BinaryFormatter bFormatter = new BinaryFormatter();
            bFormatter.Serialize(stream, matrix);
            stream.Close();

            Console.WriteLine("Matrix written to " + serializeFileName);
        }

        public int[,] DeSerializeMatrix()
        {
            // File will be the same as off-file, but with another extension
            string fn = Path.ChangeExtension(fileName, ".mtrx");
            Stream stream = File.Open(fn, FileMode.Open);
            BinaryFormatter bFormatter = new BinaryFormatter();
            int[,] matrix = (int[,])bFormatter.Deserialize(stream);
            stream.Close();

            return matrix;
        }

        static void Main(string[] args)
        {
            OffReader offReader;

            //use given argument to find the file to be read
            if (args.Length != 0) offReader = new OffReader(@args[0]);
            else offReader = new OffReader(@"C:\Users\frank\Documents\Multimedia Retrieval\MR_HKSFilereader\MR_HKSFilereader\10faces.off");

            // read the off-file
            offReader.ReadFile();

            // print vertex details
            offReader.PrintInfo();

            // create, serialize and save matrix
            offReader.SaveToSerializedMatrix();

            // deserialize matrix from saved file
            int[,] m = offReader.DeSerializeMatrix();
            Console.WriteLine("Value read from deserialised matrix at [0,0]: " + m[0, 0]);

            //exit on enter
            Console.ReadLine();
        }

        public void colorizeModel(double[] hks, double min, double max)
        {
            string offOutput = "colorized.off";
            Stream stream = File.Open(offOutput, FileMode.Create);
            StreamWriter sw = new StreamWriter(stream);

            TextReader reader = new StreamReader(fileName);

            //skip first line
            string line = reader.ReadLine();
            sw.WriteLine("[C]OFF");
            //second line contains vertices and faces counts.
            line = reader.ReadLine();
            sw.WriteLine(line);
            string[] split = line.Split(" ".ToCharArray());
            nrVertices = Convert.ToInt32(split[0]);

            //skip all vertex descriptions
            int[] colors;
            for (int i = 0; i < nrVertices; i++)
            {
                colors = getColors(hks[i], min, max);
                Console.WriteLine(hks[i] + " " + colors[0] + " " + colors[1] + " " + colors[2]);
                sw.WriteLine(reader.ReadLine() + " " + colors[0] + " " + colors[1] + " " + colors[2]);
            }
            line = reader.ReadLine();
            sw.WriteLine(line);

            //read all face lines
            while (line != null)
            {
                //read new line
                line = reader.ReadLine();
                sw.WriteLine(line);
            }

            Console.WriteLine("min: " + min + " max: " + max);

            sw.Close();
            stream.Close();
            reader.Close();
        }

        public int[] getColors(double hksValue, double min, double max)
        {
            int[] colors = new int[3];
            hksValue = (hksValue - min) / (max - min);
            hksValue = Math.Max(0, Math.Min(1, hksValue));

            // red
            colors[0] = (int)Math.Round(255 * Math.Max(0, (2 * (hksValue - 0.5))));
            // green
            //colors[1] = (int)Math.Round(255 * 2 * Math.Max(0, 0.5 - Math.Abs(hksValue - 0.5)));
            // blue
            colors[2] = (int)Math.Round(255 * Math.Max(0, 1 - (2 * hksValue)));

            // green
            colors[1] = 255 - colors[0] - colors[2];

            //Console.WriteLine("R:" + colors[0] + " G:" + colors[1] + " B:" + colors[2]);

            return colors;
        }
    }
}
